#include "Camera.h"
#include "..\base\Engine.h"

namespace ds {

Camera::Camera(void) {		
	D3DXMatrixIdentity(&m_View);
	D3DXMatrixIdentity(&m_Proj);
	D3DXMatrixIdentity(&mViewProj);
	speed = 0.1f;

	m_ViewPos = Vec3(0,0,-1);
	m_UpVec = Vec3(0,1,0);
	m_LookAt = Vec3(0,0,0);
	buildView();

	float w = (float)gEngine->getWidth();
	float h = (float)gEngine->getHeight();
	//D3DXMatrixOrthoOffCenterLH(&m_OrthoProj,0.0f,w,0.0f,h,0.0f,100.0f);
	D3DXMatrixOrthoLH(&m_OrthoProj, w, h, 0.1f, 100.0f);
	//D3DXMatrixIdentity(&m_OrthoView);
	D3DXMatrixLookAtLH(&m_OrthoView,&m_ViewPos,&m_LookAt,&m_UpVec);
	m_Ortho = false;
}

Camera::~Camera(void) {
}

void Camera::setLens(float fov, float aspect, float nearZ, float farZ) {
	D3DXMatrixPerspectiveFovLH(&m_Proj, fov, aspect, nearZ, farZ);	
	buildView();
}

void Camera::setPosition(const Vec3& pos) { 
	m_ViewPos = pos;
	buildView();
}

void Camera::lookAt(const Vec3& lookAt) { 
	m_LookAt = lookAt;
	buildView();
}

void Camera::lookAt(Vec3 pos,Vec3 target,Vec3 up) {

	Vec3 L = target - pos;
	D3DXVec3Normalize(&L, &L);

	Vec3 R;
	D3DXVec3Cross(&R, &up, &L);
	D3DXVec3Normalize(&R, &R);

	Vec3 U;
	D3DXVec3Cross(&U, &L, &R);
	D3DXVec3Normalize(&U, &U);
	worldPosition   = pos;
	worldRightPosition = R;
	worldUpPosition    = U;
	wLookVec  = L;
	buildView();	
}

void Camera::move(float unit) {
	LOG(logINFO) << "move " << unit;
	Vec3 dir;
	D3DXVec3Normalize(&dir,&(m_LookAt - m_ViewPos));
	if ( unit > 0.0f ) {
		m_ViewPos += dir * unit;
		m_LookAt += dir * unit;
	}
	else {
		m_ViewPos -= dir * unit;
		m_LookAt -= dir * unit;
	}
	//worldPosition += dir*step;
	buildView();
}

void Camera::strafe(float unit) {
	Vec3 dir(0.0f, 0.0f, 0.0f);
	float step = unit;
	if ( unit > 0.0f ) {
		dir += worldRightPosition;
	}
	else {
		step *= -1.0f;
		dir -= worldRightPosition;
	}
	worldPosition += dir*step;
	buildView();
}

void Camera::setOrthogonal() {
	IDirect3DDevice9 * pDevice = gEngine->getDevice();
	m_Ortho = true;
	mViewProj = m_OrthoView * m_OrthoProj;
	
}

void Camera::restore() {
	m_Ortho = false;
}

void Camera::setPitch(float angle) {
	D3DXMATRIX R;
	D3DXMatrixRotationAxis(&R, &worldRightPosition, angle);
	D3DXVec3TransformCoord(&wLookVec, &wLookVec, &R);
	D3DXVec3TransformCoord(&worldUpPosition, &worldUpPosition, &R);
	buildView();
}

void Camera::setYAngle(float angle) {
	// Rotate camera's look and up vectors around the camera's right vector.
	D3DXMATRIX R;
	D3DXMatrixRotationY(&R, angle);
	D3DXVec3TransformCoord(&worldRightPosition, &worldRightPosition, &R);
	D3DXVec3TransformCoord(&worldUpPosition, &worldUpPosition, &R);
	D3DXVec3TransformCoord(&wLookVec, &wLookVec, &R);
	buildView();
}

const D3DXMATRIX& Camera::getViewMatrix() const {
	if ( m_Ortho ) {
		return m_OrthoView;
	}
    return m_View;
}

const D3DXMATRIX& Camera::getProjectionMatrix() const {
	if ( m_Ortho ) {
		return m_OrthoProj;
	}
	return m_Proj;
}

const D3DXMATRIX& Camera::getViewProjectionMatrix() const {
    return mViewProj;
}

void Camera::buildView() {

	// Keep camera's axes orthogonal to each other and of unit length.
	//D3DXVec3Normalize(&wLookVec, &wLookVec);

	//D3DXVec3Cross(&worldUpPosition, &wLookVec, &worldRightPosition);
	//D3DXVec3Normalize(&worldUpPosition, &worldUpPosition);

	//D3DXVec3Cross(&worldRightPosition, &worldUpPosition, &wLookVec);
	//D3DXVec3Normalize(&worldRightPosition, &worldRightPosition);
	//Vec3 la = Vec3(0,0,0);
	D3DXMatrixLookAtLH(&m_View,&m_ViewPos,&m_LookAt,&m_UpVec);
	// Fill in the view matrix entries.
	/*
	float x = -D3DXVec3Dot(&worldPosition, &worldRightPosition);
	float y = -D3DXVec3Dot(&worldPosition, &worldUpPosition);
	float z = -D3DXVec3Dot(&worldPosition, &wLookVec);

	mView(0,0) = worldRightPosition.x; 
	mView(1,0) = worldRightPosition.y; 
	mView(2,0) = worldRightPosition.z; 
	mView(3,0) = x;   

	mView(0,1) = worldUpPosition.x;
	mView(1,1) = worldUpPosition.y;
	mView(2,1) = worldUpPosition.z;
	mView(3,1) = y;  

	mView(0,2) = wLookVec.x; 
	mView(1,2) = wLookVec.y; 
	mView(2,2) = wLookVec.z; 
	mView(3,2) = z;   

	mView(0,3) = 0.0f;
	mView(1,3) = 0.0f;
	mView(2,3) = 0.0f;
	mView(3,3) = 1.0f;
	*/
	mViewProj = m_View * m_Proj;
}

};